﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MealBooking.Core.Models.Dishes;
using System.Xml.Linq;
using System.IO;
using System.Xml.Serialization;
using MealBooking.Core.Services.Interface.DishService;
using MealBooking.Core.Models.Meals;
using MealBooking.Core.Common;

namespace MealBooking.Core.Services.Impl.DishService
{
    public class DishService : IDishService
    {
        #region private
        static string DishFolderPath = Path.Combine(Utils.Utils.StorageLocation(), "Dishes");
        #endregion

        #region Dish
        #region pirvate
        private static string DishFilePath = Path.Combine(DishFolderPath, "dishes.xml");
        private static object _dishLocker = new object();
        private XDocument LoadDishFile()
        {
            return Utils.Utils.InitXmlFile(DishFilePath);
        }
        private void SaveDishFile(XDocument doc)
        {
            Utils.Utils.SaveFile(_dishLocker, doc, DishFilePath);
        }
        #endregion
        public List<Dish> AllDishes()
        {
            var doc = LoadDishFile();
            var xmlDishes = doc.Root.Elements("Dish");
            return ModelSerializer.Deserialize<Dish>(xmlDishes);
        }
        public List<Dish> DishesWithoutDishCategory()
        {
            var doc = LoadDishFile();
            var xmlDishes = doc.Root.Elements("Dish").Where(d =>
                d.Element("Category") == null);
            return ModelSerializer.Deserialize<Dish>(xmlDishes);
        }
        public Dish DishDetail(string dishId)
        {
            Dish dish = null;
            var doc = LoadDishFile();
            var xmlDish = doc.Descendants("Dish").SingleOrDefault(d =>
               dishId.Equals(d.Element("Id").Value));
            dish = ModelSerializer.Deserialize<Dish>(xmlDish);
            return dish;
        }
        public List<Dish> DishDetails(List<string> dishIds)
        {
            var doc = LoadDishFile();
            var xmlDishes = doc.Root.Elements("Dish").Where(d =>
                dishIds.Contains(d.Element("Id").Value));
            return ModelSerializer.Deserialize<Dish>(xmlDishes);
        }
        public Dish IsDishNameInUse(string dishName)
        {
            var doc = LoadDishFile();
            //has same name:
            var sameNameDish = doc.Root.Elements("Dish").SingleOrDefault(d =>
                 dishName.Equals(d.Element("Name").Value));
            return ModelSerializer.Deserialize<Dish>(sameNameDish);
        }
        public Dish AddDish(Dish newDish)
        {
            newDish.Id = Guid.NewGuid().ToString().ToLower();
            return UpdateDish(newDish);
        }
        public Dish UpdateDish(Dish dish)
        {
            var doc = LoadDishFile();
            var xmlDish = doc.Root.Elements("Dish").SingleOrDefault(d =>
                 dish.Id.Equals(d.Element("Id").Value));
            if (xmlDish != null)
            {
                xmlDish.Remove();
            }
            if (dish.Category != null)
            {
                var category = DishCategoryDetail(dish.Category.Id);
                category = new DishCategory { Id = category.Id, Name = category.Name };
                dish.Category = category;
            }
            xmlDish = ModelSerializer.Serialize(dish);
            doc.Root.Add(xmlDish);
            SaveDishFile(doc);
            UpdateDishCategoryOfDish(dish);
            return DishDetail(dish.Id);
        }
        #endregion

        #region Dishes in Dish Category
        public List<Dish> DishesInDishCategory(string dishCategoryId)
        {
            var doc = LoadDishFile();
            var xmlDishes = doc.Root.Elements("Dish").Where(d =>
                d.Element("Category") != null &&
                dishCategoryId.Equals(d.Element("Category").Element("Id").Value));
            return ModelSerializer.Deserialize<Dish>(xmlDishes);
        }
        public void UpdateDishesInDishCategory(DishCategory dishCategory)
        {
            if (dishCategory == null)
            {
                return;
            }
            var doc = LoadDishFile();
            var xmlDishes = doc.Root.Elements("Dish");
            var oldxmlDishes = xmlDishes.Where(d =>
                d.Element("Category") != null && dishCategory.Id.Equals(d.Element("Category").Element("Id").Value));
            if (oldxmlDishes != null && oldxmlDishes.Count() != 0)
            {
                foreach (var oldXmlDish in oldxmlDishes)
                {
                    oldXmlDish.Element("Category").Remove();
                }
            }
            var frag = new XElement("Category", new XElement("Id", dishCategory.Id), new XElement("Name", dishCategory.Name));
            var dishes = dishCategory.Dishes;
            if (dishes != null && dishes.Count != 0)
            {
                foreach (var dish in dishes)
                {
                    var xmlDish = xmlDishes.SingleOrDefault(d =>
                    dish.Id.Equals(d.Element("Id").Value));
                    if (xmlDish.Element("Category") != null)
                    {
                        xmlDish.Element("Category").Remove();
                    }
                    xmlDish.Add(frag);
                }
            }
            SaveDishFile(doc);
        }
        #endregion

        #region Dishes in Meal Category
        public Dictionary<DishCategory, List<Dish>> DishesInMealCategory(string mealCategoryId)
        {
            var doc = Utils.Utils.InitXmlFile(DishFilePath);
            Dictionary<DishCategory, List<Dish>> dishesInMealCategory = new Dictionary<DishCategory, List<Dish>>();
            var dishCategories = DishCategoriesInMealCategory(mealCategoryId);
            if (dishCategories == null || dishCategories.Count == 0)
            {
                return null;
            }
            foreach (var dishCategory in dishCategories)
            {
                if (dishCategory == null)
                {
                    continue;
                }
                var dishes = DishesInDishCategory(dishCategory.Id);
                if (dishes == null || dishes.Count == 0)
                {
                    continue;
                }
                dishesInMealCategory[dishCategory] = dishes;
            }
            return dishesInMealCategory;
        }
        #endregion

        #region DishCategory

        #region private
        static string DishCategoryFilePath = Path.Combine(DishFolderPath, "DishCategories/DishCategories.xml");
        private static object _dishCategoryLocker = new object();
        #endregion

        #region private method
        private XDocument LoadDishCategoryFile()
        {
            return Utils.Utils.InitXmlFile(DishCategoryFilePath);
        }
        private void SaveDishCategoryFile(XDocument doc)
        {
            Utils.Utils.SaveFile(_dishCategoryLocker, doc, DishCategoryFilePath);
        }
        #endregion

        public List<DishCategory> AllDishCategories()
        {
            var doc = LoadDishCategoryFile();
            var xmlDishCategories = doc.Root.Elements("DishCategory");
            return ModelSerializer.Deserialize<DishCategory>(xmlDishCategories);
        }
        public DishCategory DishCategoryDetail(string dishCategoryId)
        {
            var doc = LoadDishCategoryFile();
            var xmlDishCategory = doc.Root.Elements("DishCategory").SingleOrDefault(c =>
                dishCategoryId.Equals(c.Element("Id").Value));
            return ModelSerializer.Deserialize<DishCategory>(xmlDishCategory);
        }
        public List<DishCategory> DishCategoryDetails(List<string> dishCategoryIds)
        {
            var doc = LoadDishCategoryFile();
            var xmlDishCategories = doc.Root.Elements("DishCategory").Where(c =>
                dishCategoryIds.Contains(c.Element("Id").Value));
            return ModelSerializer.Deserialize<DishCategory>(xmlDishCategories);
        }
        public DishCategory IsDishCategoryNameInUse(string newDishCategoryName)
        {
            var doc = LoadDishCategoryFile();
            var xmlDishCategory = doc.Root.Elements("DishCategory").SingleOrDefault(c =>
                 newDishCategoryName.Equals(c.Element("Name").Value));
            return ModelSerializer.Deserialize<DishCategory>(xmlDishCategory);
        }
        public DishCategory AddDishCategory(DishCategory newDishCategory)
        {
            if (newDishCategory == null)
            {
                return null;
            }
            newDishCategory.Id = Guid.NewGuid().ToString().ToLower();
            return UpdateDishCategory(newDishCategory);
        }
        public DishCategory UpdateDishCategory(DishCategory dishCategory)
        {
            if (dishCategory == null)
            {
                return null;
            }
            var doc = LoadDishCategoryFile();
            var xmlDishCategory = doc.Root.Elements("DishCategory").SingleOrDefault(c =>
                dishCategory.Id.Equals(c.Element("Id").Value));
            if (xmlDishCategory != null)
            {
                xmlDishCategory.Remove();
            }
            #region dishes
            UpdateDishesInDishCategory(dishCategory);
            #endregion
            if (dishCategory.Dishes != null && dishCategory.Dishes.Count != 0)
            {
                var oldDishIds = dishCategory.Dishes.Select(d => d.Id).ToList();
                var oldXmlDishes = doc.Root.Descendants("Dish").Where(d =>
                    oldDishIds.Contains(d.Element("Id").Value));
                oldXmlDishes.Remove();
                var dishes = DishDetails(oldDishIds);
                dishCategory.Dishes = dishes.Select(d => new Dish { Id = d.Id, Name = d.Name }).ToList();
            }
            if (dishCategory.Category != null)
            {
                var mealCategory = CoreService.MealService.MealCategoryDetail(dishCategory.Category.Id);
                dishCategory.Category = new MealCategory { Id = mealCategory.Id, Name = mealCategory.Name };
            }
            xmlDishCategory = ModelSerializer.Serialize(dishCategory);
            doc.Root.Add(xmlDishCategory);
            SaveDishCategoryFile(doc);

            if (dishCategory.Category != null)
            {
                CoreService.MealService.AddDishCategoryToMealCategory(dishCategory.Category.Id, dishCategory);
            }

            return DishCategoryDetail(dishCategory.Id);
        }
        #endregion

        #region DishCategory of Dish
        public void UpdateDishCategoryOfDish(Dish dish)
        {
            var doc = LoadDishCategoryFile();
            var oldXmlDish = doc.Root.Descendants("Dish").SingleOrDefault(d =>
                dish.Id.Equals(d.Element("Id").Value));
            if (oldXmlDish != null)
            {
                oldXmlDish.Remove();
            }
            if (dish.Category != null)
            {
                var xmlDishCategory = doc.Root.Elements("DishCategory").SingleOrDefault(c =>
                    dish.Category.Id.Equals(c.Element("Id").Value));
                var xmlDishes = xmlDishCategory.Element("Dishes");
                if (xmlDishes == null)
                {
                    xmlDishes = new XElement("Dishes");
                    xmlDishCategory.Add(xmlDishes);
                }
                var newDish = new Dish { Id = dish.Id, Name = dish.Name };
                var xmlDish = ModelSerializer.Serialize(newDish);
                xmlDishes.Add(xmlDish);
            }
            SaveDishCategoryFile(doc);
        }
        #endregion

        #region DishCategories in MealCategory
        public List<DishCategory> DishCategoriesInMealCategory(string mealCategoryId)
        {
            var doc = LoadDishCategoryFile();
            var xmlDishCategories = doc.Root.Elements("DishCategory").Where(c =>
                c.Element("Category") != null &&
              mealCategoryId.Equals(c.Element("Category").Element("Id").Value));
            return ModelSerializer.Deserialize<DishCategory>(xmlDishCategories);
        }
        public void UpdateDishCategoriesInMealCategory(MealCategory mealCategory)
        {
            if (mealCategory == null)
            {
                return;
            }
            var doc = LoadDishCategoryFile();
            var xmlDishCategories = doc.Root.Elements("DishCategory");
            var oldXmlDishCategories = xmlDishCategories.Where(c =>
                c.Element("Category") != null && mealCategory.Id.Equals(c.Element("Category").Element("Id").Value));
            if (oldXmlDishCategories != null && oldXmlDishCategories.Count() != 0)
            {
                oldXmlDishCategories.ToList().ForEach(c => c.Element("Category").Remove());
            }
            var dishCategories = mealCategory.DishCategories;
            if (dishCategories != null && dishCategories.Count() != 0)
            {
                var frag = new XElement("Category", new XElement("Id", mealCategory.Id), new XElement("Name", mealCategory.Name));
                foreach (var dishCategory in dishCategories)
                {
                    var xmlDishCategory = xmlDishCategories.SingleOrDefault(c =>
                        dishCategory.Id.Equals(c.Element("Id").Value));
                    if (xmlDishCategory.Element("Category") != null)
                    {
                        xmlDishCategory.Element("Category").Remove();
                    }
                    xmlDishCategory.Add(frag);
                    doc.Root.Add(frag);
                }
            }
            SaveDishCategoryFile(doc);
        }
        #endregion
    }
}
